<?

class mysqlCheck{
	//Variables

	function Needed() {return  Array("Id"=> "int", "tmsCreate"=> "int","userCreate"=> "int","tmsEdit"=> "int","userEdit"=>"int","uid"=>"int","gid"=>"int","gmod"=>"int","umod"=>"int","omod"=>"int");}
	function tabType() {return  Array("int","string","varchar","boolean","password","image","order","date","text");}
	//----------------------------------------------//
	// VERIFICATION									//
	//----------------------------------------------//

	//Fonction associee a Check qui verifie les donnees entre sql et le schema
	//Si elle n'existe pas on la cree, et si la creation ne marche pas on renvoie une erreur
	function initData () {
		if (!mysqlCheck::getStructureSql()) mysqlCheck::generateSqlTable();
		mysqlCheck::Compare();
	}

	//Compare sql et le schema
	function Compare() {
		$erreur = false;
		//On compare les proprietes et les clefs etrangeres
		if (mysqlCheck::compareProperties() || mysqlCheck::compareFKeys()){
			mysqlCheck::getStructureSql();
			if (mysqlCheck::compareProperties() || mysqlCheck::compareFKeys()){
				$erreur = true;
			}
		}
		//On teste si il existe un heritage potentiel
		if ($this->Heritage && !mysqlInherit::verifyInheritStruct()) {
			mysqlInherit::createInheritStruct();
		}
		//On teste si l objectClass est reflexif
		if ($this->isReflexive() && !mysqlInterval::verifyIntervalIndex()) {
			//Dans ce cas on met en place les index intervallaires
			mysqlInterval::createIntervalIndex();
		}
		// @todo : Création des indexs
		return $erreur;
	}

	//Renvoie la structure de la table et si elle echoue appelle la creation dune nouvelle table.
	function createData() {
		$sql="SHOW COLUMNS FROM `".$this->Prefix.$this->titre."`";
		if (!mysql_query($sql)) 
		mysqlCheck::generateSqlTable();
	}

	//Renvoie la structure SQL
	function getStructureSql() {
		//On execute la requete qui renvoie la structure
		$sql="SHOW COLUMNS FROM `".$this->Prefix.$this->titre."`";
		$resql=$this->executeSql($sql,"CHECK");
		if (empty($resql)) return false;
		$i = 0;
		//On range le resultat dans un tableau
		foreach($resql as $tab){
			//La requete s'est execute avec succes: on remplit le tablea
			if (list($type,$length)=split("[(]|[)]", $tab['Type'])){
				//Si c'est un varchar, on separe lenght de type avec une expression reg
				if (preg_match("#varchar#",$tab['Type'])){
					$this->tableSql[$tab['Field']]['length'] = $length;
					$this->tableSql[$tab['Field']]['type'] = $type;
				}else  $this->tableSql[$tab['Field']]['type'] = $type;
			}else $this->tableSql[$i]['type'] = $tab['Type'];
			//On transforme les booleens de SQL en booleen classique
			$this->tableSql[$tab['Field']]['null'] = ($tab['Null']=="YES") ? 1:0;
			$this->tableSql[$tab['Field']]['default'] = $tab['Default'];
			$i++;
		}
		return true;
	}


	//Cree un nouveau champ: 'propriete' ou clef 'etrangere' selon le type mentionne.
	function createNewField ($Nom,$type,$Prefixe="") {
		$sql = 'ALTER TABLE `'.$this->Prefix.$this->titre.'` ADD ';
		if ($type == 'propriete') $sql .= mysqlCheck::writeProperty($Nom,$Prefixe);
		if ($type == 'systeme') $sql .= mysqlCheck::writeSysProperty($Nom,$Prefixe);
		elseif($type == 'etrangere'){
			$sql .= mysqlCheck::writeForeignKey($Nom);
		}
		//Si la requete est OK, on renvoie true et un message. Sinon, on renvoie false et une erreur.
		if ($this->executeSql($sql,"MODIF")) return true;
		else{
// 			$GLOBALS['Systeme']->Error->sendWarningMsg(2,"ERREUR 2:CREATENEWFIELD");
			return false;
		}
	}

	//Cette methode pre-ecrit les requetes SQL au sujet des proprietes
	function writeProperty($Nom,$Prefixe="") {
		$Tab= mysqlCheck::initSpecialTypes($this->Proprietes[$Nom]);
		if ($Prefixe != NULL) $sql= '`'.$Prefixe.'-'.$Nom.'` ';
		else $sql= '`'.$Nom.'` ';
		$sql.= ($Tab['type'] == 'varchar') ? $Tab['type'].'('.(($Tab['length']!="")?$Tab['length']:"255").') ': $Tab['type'].' ';
		//La possibilite d'etre vide:
		if ($Tab['null'] == "false") $sql.='NOT';
		$sql.=' NULL';
		//La valeur par defaut:
		if ($Tab['default'] != NULL&&sizeof(explode("::",$Tab['default']))==1) $sql.= ' default "'.$Tab['default'].'"';
		//Si le type est une reference alors il faut faut faire 3 champs supplementaires
		if ($Tab["special"]=="Reference"){
			//Le type et la longueur:
			$sql.= ',ADD `Module` varchar(50) NULL,ADD `ObjectClass` varchar(50) NULL,ADD `Value` INT NULL';
		}
		return $sql;
	}

	//Cette methode pre-ecrit les requetes SQL au sujet des proprietes
	function writeSysProperty($Nom,$Prefixe="") {
		$Needed = mysqlCheck::Needed();
		$Tab=$Needed[$Nom];
		$sql= '`'.$Nom.'` ';
		//Le type et la longueur:
		$sql.= $Tab;
		//La valeur par defaut:
		return $sql;
	}

	//Cette methode pre-ecrit les requetes SQL au sujet des clefs secondaires
	function writeForeignKey($Nom) {
		$Tab=$this->Etrangeres[$Nom];
		//ANalyse de data
		$t=explode(",",$Tab["data"]);
		if ($t[1]!="Id"){
			//On recupere le type de la cible
			$K = $GLOBALS["Systeme"]->Modules[$this->Module]->Db->findByTitle($t[0]);
			$P = $GLOBALS["Systeme"]->Modules[$this->Module]->Db->ObjectClass[$K]->Proprietes[$t[1]];
			$P["Nom"] = $Nom;
			$sql = mysqlCheck::writeProperty($P);
		}else{
			//On leur donne un nom
			$sql= '`'.$Nom.'`';
			//On leur donne un NULL correspondant a leur cardinalites
			$sql.= ($Tab['card'] == "1,1" || $Tab['card'] == "1,n") ? 'INT NOT NULL': 'INT NULL';
		}
		return $sql;
	}

	//Cette methode met a jour les tables MySql, colonne par colonne.
	function alterSql($nom, $type="prop") {
		$sql = 'ALTER TABLE `'.$this->Prefix.$this->titre.'` MODIFY ';
		//Si c'est une clef etrangere, on precise juste son nom
		switch($type)
		{
			case "fkey":
				$sql.= '`'.$nom.'` INT ';
				if ($this->Etrangeres[$nom]['card'] == "1,1") $sql.='NOT';
				$sql.=' NULL';
				break;
			case "prop":
				$sql.= mysqlCheck::writeProperty($nom);
				break;
			case "sys":
				$sql.= mysqlCheck::writeSysProperty($nom);
				break;
		}
// 		echo "SQL ---- > $sql\r\n<br/>";
		if ($this->executeSql($sql,"MODIF")) return true;
		else{
// 			$GLOBALS['Systeme']->Error->sendWarningMsg(2,"ERREUR 2 :ALTERSQL");
			return false;
		}
	}

	//Methode qui cree les tables SQL correspondants au contenu de la classe.
	function generateSqlTable() {
		$sql  = 'CREATE TABLE IF NOT EXISTS `'.$this->Prefix.$this->titre.'` (';
		$sql.= '`Id` INT NOT NULL AUTO_INCREMENT,';
		$sql.= '`tmsCreate` INT NOT NULL,';
		$sql.= '`userCreate` INT NOT NULL,';
		$sql.= '`tmsEdit` INT NOT NULL,';
		$sql.= '`userEdit` INT NOT NULL,';
		$sql.= ' uid int(11) default NULL,';
		$sql.= ' gid int(11) default NULL,';
		$sql.= ' umod int(11) default NULL,';
		$sql.= ' gmod int(11) default NULL,';
		$sql.= ' omod int(11) default NULL,';
		//Puis on insere les proprietes
		foreach ($this->Proprietes as $Key=>$Prop){
			//D'abord le nom
			if (empty($Prop["Ref"])){
				if ($Key!="Id") if ($Flag) $sql.= ' ,';
				if ($Key!="Id") $sql.= mysqlCheck::writeProperty($Key);
				$Flag = true;
			}
		}
		//Ensuite, on insere les clefs etrangeres
		foreach ($this->Etrangeres as $Key=>$Etr){
			if (in_array($Etr['card'],$this->isShortKey)) {
				if ($Flag) $sql.= ',';
				$sql .= mysqlCheck::writeForeignKey($Key);
				$Flag = true;
			}
		}
		//On insere la clef primaire et on finit la requete SQL
		$sql.= ', UNIQUE (`Id`) )';
		//On execute la requete: si elle s'est bien passe = 1, sinon =0
		if (mysqlDriver::executeSql($sql,"CREATE")) return mysqlCheck::fillTable();
		else{
// 			$GLOBALS['Systeme']->Error->sendWarningMsg(2,"ERREUR 2 :RENAMESQL");
			return false;
		}
	}


	//Compare les proprietes du tableau avec celles de la table
	function compareProperties() {
		//On charge le tableau qui contient les conversions possibles
		$Langues = $GLOBALS["Systeme"]->Conf->get("GENERAL::LANGUAGE");
		$conversion['varchar'] = 'text';
		$conversion['bool'] = 'int';
		$conversion['int']= 'varchar';
		//On verifie les proprietes systemes
		$Needed = mysqlCheck::Needed();
		foreach($Needed as $Nom => $Prop){
			if (@!array_key_exists($Nom,$this->tableSql)) {
				//OK
// 				echo "<li>--#> On cree un nouveau champ $Nom </li>";
				mysqlCheck::createNewField($Nom, 'systeme');
			}
		}
		foreach($this->Proprietes as $Nom => $Prop){
			$Prop = mysqlCheck::initSpecialTypes($Prop);
			if (!empty($Prop["Ref"])) continue;
			//Compteur de changement
			$noChange = 0;
			//Compteur de creation
			if (is_array($this->tableSql))if (array_key_exists($Nom,$this->tableSql)) {
				//Le type:
				if ($Prop['type'] == $this->tableSql[$Nom]['type']) $noChange++;
				//La longueur:
				if ($Prop['length'] == $this->tableSql[$Nom]['length']) $noChange++; else {
// 					echo "ERREUR LONGUEUR ".$Prop['length']." != ".$this->tableSql[$Nom]['length']."<br />\r\n";
				}
				//La valeur par defaut:
				if ($Prop['default'] ==  $this->tableSql[$Nom]['default']) $noChange++;
			}else{
// 				echo "<li>--##> On cree un nouveau champ $Nom </li>";
				mysqlCheck::createNewField($Nom, 'propriete');
				//die("REFRESH PLEASE ...");
				//return true;
			}
			//Cas du changement de langue
			foreach ($Langues as $Prefixe=>$Langue){
				if ($Prop["special"]=="multi"){
					if (!@array_key_exists($Prefixe."-".$Nom,$this->tableSql)&&$Langue["DEFAULT"]!=1) {
// 						echo "<li>NEW FIELD $Prefixe-$Nom </li>";
						mysqlCheck::createNewField($Nom,'propriete',$Prefixe);
					}
				}
			}
			//Si un des noChange n'a pas ete incremente, le champ est mis a jour.
			if ($noChange != 3) {
// 				echo $Nom."--------------------\r\n";
				mysqlCheck::alterSql($Nom);
				return true;
			}
		}
		return false;
	}

	function Verify(){
		//Verifie la valeur du schema
		$error = 0;
		foreach ($this->Proprietes as $Key=>$Value){
			$Tab = mysqlCheck::initSpecialTypes($Value);
			if($Tab['type'] == "varchar"&&$Tab['length']=="") die ("Le champ $Key n'a pas de longueur.");
			//On verifie la presence d'un nom et d'un type
			if($Tab['type'] == NULL) die ("Le champ $Key n'a pas de type.");
			//Si la longueur est superieure a 255
			if($Tab['length'] > 255) die ("Le champ $Key n'a pas de longueur.");
			echo "<li>Check properties ".$Key." $error </li>";
		}
		//Si il n'y a pas eu d'erreur, on valide, sinon on renvoie une erreur.
		return (!$error) ?  true : false;
	}



	function initSpecialTypes($Tab){
		//Donne une valeur type sql aux types speciaux
		switch (strtolower($Tab['type'])){
                    /**/
			case "image":				//Url locale avec generation de miniature a l insertion ou la modification
				$Tab['type']="varchar";
				$Tab['special']='Img';
                                $Tab['displayType']="media";
				break;
			case "password":			//generation de mot de passe en md5
				$Tab['type']="varchar";
				$Tab['length']="32";
				$Tab['special']='password';
                                $Tab['displayType']="normal";
				break;
			case "alias":				//Uniquement pour les menus permet de faire pointer une url vers une requete
				$Tab['type']="varchar";
				$Tab['special']='alias';
                                $Tab['displayType']="normal";
				break;
			case "objectclass":			//Liaison vers une objectClasss particulier
				$Tab['type']="varchar";
                                $Tab['displayType']="normal"; 
				// 				$Tab['special']='multi';
				break;
			case "titre":				//Titre d une donnï¿œe (declinable en plusieurs langues)
				$Tab['type']="varchar";
				$Tab['special']='multi';
                                $Tab['displayType']="line";
				break;
			case "color":				//Titre d une donnï¿œe (declinable en plusieurs langues)
				$Tab['type']="varchar";
				$Tab['length']="7";
                                $Tab['displayType']="normal";
				break;
			case "random":				//generation d une chaine aleatoire pour les verifications
				$Tab['type']="varchar";
				$Tab['special']='random';
				$Tab['length']="20";
                                $Tab['displayType']="normal";
				break;
			case "textonly":				//Champ texte
			case "text":				//Champ texte
			case "txt":				//Champ texte
				$Tab['type']="text";
                                $Tab['displayType']="block";
				//$Tab['special']='multi';
				break;
			case "metat":				//champ meta-titre
				$Tab['type']="varchar";
				$Tab['length']="200";
                                $Tab['displayType']="normal";
				break;	
			case "metad":				//champ meta-description
				$Tab['type']="varchar";
				$Tab['length']="250";
                                $Tab['displayType']="normal";
				break;
			case "html":				//Champ html
			case "bbcode":				//Champ bbcode
				$Tab['type']="text";
				$Tab['special']='multi';
                                $Tab['displayType']="block";
				break;
			case "id":
				$Tab['type']="int";
				$Tab['special']='id';
                                $Tab['displayType']="normal";    
				break;
			case "autodico":			//Enrichissement d un dictionnaire
				$Tab['type']="int";
				$Tab['special']='autodico';
                                $Tab['displayType']="normal";
				break;
			case "mail":				//Champ mail avec verification
			case "url":				//Url
			case "private":				//Champ important
				$Tab['type']="varchar";
                                $Tab['displayType']="normal";
				break;
			case "order":				//Champ ordre
				$Tab['type']="int";
				$Tab['special']='order';
                                $Tab['displayType']="normal";
				break;
			case "date":				//Champ date
				$Tab['type']="int";
				$Tab['special']='Date';
                                $Tab['displayType']="normal";
				break;
			case "price":				//Champ date
			case "pourcent":				//Champ date
				$Tab['type']="double";
				$Tab['special']='Date';
                                $Tab['displayType']="normal";
				break;
			case "link":				//Generation d un titre routable sur internet a partir du searchOrder
				$Tab['type']="varchar";
				$Tab['special'] = "multi";
				$Tab['content'] = "link";
                                $Tab['displayType']="normal";
				break;
			case "canonic":				//Generation d un titre routable sur internet a partir du searchOrder
				$Tab['type']="varchar";
				$Tab['special'] = "multi";
				$Tab['content'] = "canonic";
                                $Tab['displayType']="normal";
				break;
			case "langfile":				//Upload de fichier
				$Tab['type']="varchar";
				$Tab['length']="255";
				$Tab['special']='multi';
                                $Tab['displayType']="normal";
				break;
			case "int":				//Upload de fichier
				$Tab['type']="int";
                                $Tab['displayType']="normal";
			break;
			case "varchar":				//Upload de fichier
                                $Tab['displayType']="normal";
			break;
			default:
			case "file":				//Upload de fichier
			case "string":				//Chaine de caractï¿œre
				$Tab['type']="varchar";
                                $Tab['displayType']="normal";
			break;
			case "boolean":				//Chaine de caractï¿œre
				$Tab['type']="bool";
                                $Tab['displayType']="normal";
			break;
		}
		if ($Tab['type']=='varchar')$Tab['length']=($Tab["length"]!="")?$Tab["length"]:"255";
		return $Tab;
	}


	//Compare les cles etrangeres du tableau avec celles de la table.
	function compareFKeys(){
		foreach ($this->Etrangeres as $Nom=>$Etr){
			if (!array_key_exists($Nom,$this->tableSql)&&($Etr['card']=="0,1"||$Etr['card']=="1,1")) {
				//Si le champ n'existe pas, on le cree.
				mysqlCheck::createNewField($Nom,'etrangere');
			}
		}
		return false;
	}
}